/*
    - 数组是一个逻辑上连续的内存块， 其中存储相同数据类型的元素。
    - 因为实际上是虚拟地址映射到物理地址，所以数组的物理地址可能不是连续的。
    - 数组名被指向数组的第一个元素的指针。
    - 所以数组传递的时候，可以直接传递数组名（数组的首地址），意味着传递了一个指针，在函数中可以直接修改原数组的值。

    - C++在编译时和运行时通常不会对数组访问进行边界检查。所以要注意数组越界问题

    - 数组的一些问题 ：
        - 数组要初始化，否则被分配的内存可能存在其他程序留下的脏数据。
        - 动态数组，要记得释放内存，做好内存管理。
        - 多维数组在内存中是按行优先顺序存储的
        - 数组作为函数参数时，传递的是首地址，函数内部修改会影响原始数组。
        - 数组的大小在声明时确定，不能动态改变。--》 动态数组实际上是创建一个新的更大的数组，然后把原数组的值复制进去，之后把原数组删除。
        - 栈上分配的数组：
            栈上分配的数组是指在函数或方法内部声明的数组，其内存由编译器自动分配在栈上。
            栈是一种后进先出（LIFO）的数据结构，用于存储函数调用时的局部变量和参数。
            在函数调用结束后会自动释放，不需要手动释放。
        - 堆、栈、动态、静态数组。（ob）
        - 数组的下标是从0开始。

*/

#include <iostream>
#include <algorithm> // for_each
#include <vector>    // for std::vector
using namespace std;

// 数组的定义和初始化====================================================
void array_init()
{

    // 数据类型 数组名[数组长度];
    // 数据类型 数组名[数组长度] = {值1，值2...};
    // 数据类型 数组名[] = {值1，值2...};

    int arr1[5];                  // 没有初始化，数组元素未定义，可能存在未初始化的元素。
    int arr2[5] = {1, 2, 3, 4};   // 未定义的元素会被初始化为0
    int arr3[] = {1, 2, 3, 4, 5}; // 会根据{}内的值自动计算数组长度

    arr1[0] = 10; // 通过下标的方式访问和赋值。
}

void print_four(int ele) { cout << ele << " "; }

// 遍历数组=============================================5种方法
/*
- 普通for循环：适用于所有版本的C++，需要手动计算数组长度。
- 指针遍历：适用于所有版本的C++，直接操作指针。
- 范围for循环：C++11及以上版本，语法简洁，自动遍历数组元素。
- std::for_each算法：需要包含<algorithm>头文件，适用于所有版本的C++。
- std::vector的迭代器：适用于使用std::vector的情况，提供灵活的迭代方式。
*/

void traverse_array()
{
    int arr[5] = {1, 2, 3, 4, 5};
    // sizeof(arr) / sizeof(arr[0]) 计算数组的长度
    // sizeof(arr),返回整个数组长度， sizeof(arr[0]),返回数组中单个元素的长度
    int len = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < len; i++)
    {
        // todo
    }

    // 或者使用指针的方式遍历数组
    for (int *ptr = arr; ptr < arr + len; ptr++)
    {
        // todo
    }

    // C++11 新特性，遍历数组元素，并对每个元素进行引用
    for (int &ref : arr) // C++11 新特性，遍历数组元素，并对每个元素进行引用
    {
        cout << ref << " "; // 输出数组元素的值
    }

    // 使用std::for_each算法
    for_each(arr, arr + len, print_four); // 输出数组元素的值

    // 使用std::vector 迭代器
}

void traverse_array()
{
    vector<int> vec = {1, 2, 3, 4, 5};

    for (auto it = vec.begin(); it != vec.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;
}

// 多维数组 ================================================

// 二维数组：类型 数组名[行数][列数];
// 三维数组：类型 数组名[层数][行数][列数];  （0.0）
// 四维数组：类型 数组名[超维数][层数][行数][列数];  （0_0）

/*  二维数组的几种定义方式：
数据类型 数组名[ 行数 ][ 列数 ];
数据类型 数组名[ 行数 ][ 列数 ] = { {数据1，数据2 } ，{数据3，数据4 } };
数据类型 数组名[ 行数 ][ 列数 ] = { 数据1，数据2，数据3，数据4};
数据类型 数组名[ ][ 列数 ] = { 数据1，数据2，数据3，数据4};
*/

// 二维数组名：
void TwoDArray()
{
    int arr[2][3] =
        {
            {1, 2, 3},
            {4, 5, 6}};

    cout << "二维数组大小： " << sizeof(arr) << endl;
    cout << "二维数组一行大小： " << sizeof(arr[0]) << endl;
    cout << "二维数组元素大小： " << sizeof(arr[0][0]) << endl;

    cout << "二维数组行数： " << sizeof(arr) / sizeof(arr[0]) << endl;
    cout << "二维数组列数： " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;

    // 地址
    cout << "二维数组首地址：" << arr << endl;
    cout << "二维数组第一行地址：" << arr[0] << endl;
    cout << "二维数组第二行地址：" << arr[1] << endl;

    cout << "二维数组第一个元素地址：" << &arr[0][0] << endl;
    cout << "二维数组第二个元素地址：" << &arr[0][1] << endl;
}

//=====================动态数组

// 动态数组的定义和初始化
void dynamic_array()
{
    int *arr = new int[5]; // 申请内存空间，并返回数组首地址
    arr[0] = 10;
    // 释放内存
    delete[] arr;
}

// 或者使用 vector

// ===============数组与指针

void arrayAndPointer()
{
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr; // 数组名被转换为指针

    // 此时 ptr 指向数组的第一个元素的地址
    int a = *ptr;       // 取数组第一个元素的值
    int b = *(ptr + 1); // 取数组第二个元素的值
}

void arrayAndPointer2()
{
    int num = 10;        // 声明一个整数变量
    int *ptr = &num;     // 声明一个指向整数的指针，并初始化为num的地址
    int **ptrPtr = &ptr; // 声明一个指向指针的指针，并初始化为ptr的地址

    // 输出变量、指针以及它们的值和地址
    cout << "num的值： " << num << endl;
    cout << "num的地址： " << &num << endl;
    cout << "ptr的值（存储的是num的地址）： " << ptr << endl;
    cout << "ptr的地址： " << &ptr << endl;
    cout << "ptrPtr的值（存储的是ptr的地址）： " << ptrPtr << endl;
    cout << "ptrPtr的地址： " << &ptrPtr << endl;

    // 解引用示例
    cout << "*ptr的值（num的值）： " << *ptr << endl;
    cout << "*ptrPtr的值（ptr的值，即num的地址）： " << *ptrPtr << endl;
    cout << "**ptrPtr的值（num的值）： " << **ptrPtr << endl;
}